COSC 220 Computer Science II

 

Writing and Using Makefiles


In this lab, you will learn how to write and use makefiles. A makefile is a file that contains instructions for compiling your program. The make utility uses the makefile to determine what needs to be done to compile your program after you make changes to the source code. Using a makefile does two things for you:

  1. It keeps you from having to remember all the steps required for compilation of a program that has many different files.
  2. It saves you a lot of typing.

Once you modify some source files, and type the command make, your program will be recompiled using as few compilation commands as possible. Only the files you modified and those dependent upon them will be recompiled. The dependencies and compilation commands are in the makefile.

Makefiles

A makefile is a text file that contains lines like the following:
   target:  file1 file2 ....
   <tab>what-to-do-to-make-the-target
These state that the target should be remade whenever any one of the files changes and also tell how to remake the target.

For example:

     main.o: main.cpp
     <tab>g++ -c main.cpp
     
This rule states that main.o has to be remade whenever main.cpp is modified. To make main.o, run the command gcc -c main.cpp.
Note that the command line begins with a TAB character. That's what the <tab> means (use the Tab key, don't write <tab>). The make utility absolutely requires this tab character; spaces won't do. This nutty requirement for using a tab is left over from the "good old days." Oh, well.

Note also that any line that begins with a # sign is a comment and is ignored by the make utility.


How To Use Makefiles

When make is executed, it reads the makefile looking for a target. There are two ways to execute make

  1. Type make. In this case, the first target in the makefile will be used (reading from top-to-bottom).
  2. Type make target. In this case, the named target will be used.
It checks each of the dependencies that appear in the rule. If a given dependency must be re-made, it finds the target and does the associated command. After all the dependencies have been handled, the command for the original target is executed.

A Simple Example

Here's a makefile suitable for compiling a program consisting of a single source file
# Sample makefile 

# main is the executable we want to make
# It depends on main.o
# This rule gives the linking command, once main.o 
# is up to date.
# This is the first target in the makefile, so it will be
# hit when the user types 'make'
main: main.o
        g++ main.o -o main

# main.o depends on main.cpp
# This rule gives the compilation command
main.o: main.cpp
        g++ -c main.cpp

# cleaning up the directory
clean:
        rm main.o
Note that the clean rule is not used to compile the program, but it may be used to remove the object files created. To cause the clean rule to be used, type make clean. If make encounters a rule that has no dependencies (such as the clean rule), it simply runs the command.

A More Complex Example

Most real-world programs consist of more than one source file. Remembering what needs to be re-compiled when a given source file is changed, and how to do the compilation, can be a difficult task for large programs. Using a makefile simplifies the task considerably.

Here is an example of such a makefile:

# Makefile for multiple source files, main.cpp, file1.h, 
# file1.cpp, file2.h, and file2.cpp

# prog is the executable we want to make.
prog: main.o file1.o file2.o
        g++ main.o file1.o file2.o -o prog

# rule for making  main.o
main.o: main.cpp file1.h file2.h
        g++ -c main.cpp

# rule for making  file1.o
file1.o: file1.cpp file1.h
        g++ -c file1.cpp

# rule for making file2.o
file2.o: file2.cpp file2.h
        g++ -c file2.cpp

# rule for cleaning up
clean:
       rm main.o file1.o file2.o

Note that included header files are listed among the dependencies. If one of these header files changes, the files that include it might need a re-compilation too.


Tasks

  1. Save the files foo.h, foo.cpp, bar.h, bar.cpp, and prog.cpp to your lab folder.
  2. Read the files to determine how to compile them into a program named prog.
  3. Write a makefile to perform the compilation. Model it on the makefile example given above. Make sure you have a target for clean.
  4. Test your makefile as follows: